Error-Guards
An error-guard is (an expression that evaluates to) a vector of error numbers (see APL Error Messages), followed by the digraph: ::, followed by an expression, the body of the guard, to be evaluated as the result of the function. For example:
11 5 :: ⍵×0 ⍝ Trap DOMAIN and LENGTH errors.
In common with :Trap and ⎕TRAP, error numbers 0 and 1000 are catch-alls for synchronous errors and interrupts respectively.
When an error is generated, the system searches dynamically through the calling functions for an error-guard that matches the error. If one is found, the execution environment is unwound to its state immediately prior to the error-guard's execution and the body of the error-guard is evaluated as the result of the function. This means that, during evaluation of the body, the guard is no longer in effect and so the danger of a hang caused by an infinite "trap loop", is avoided.
Notice that you can provide "cascading" error trapping in the following way:
0::try_2nd 0::try_1st expr
In this case, if expr generates an error, its immediately preceding: 0:: catches it and evaluates try_1st leaving the remaining error-guard in scope. If try_1st fails, the environment is unwound once again and try_2nd is evaluated, this time with no error-guards in scope.
See also Guards.
Examples:
Open returns a handle for a component file. If the exclusive tie fails, it attempts a share-tie and if this fails, it creates a new file. Finally, if all else fails, a handle of 0 is returned.
open←{ ⍝ Handle for component file ⍵. 0::0 ⍝ Fails:: return 0 handle. 22::⍵ ⎕FCREATE 0 ⍝ FILE NAME:: create new one. 24 25::⍵ ⎕FSTIE 0 ⍝ FILE TIED:: try share tie. ⍵ ⎕FTIE 0 ⍝ Attempt to open file. }
An error in div causes it to be called recursively with improved arguments.
div←{ ⍝ Tolerant division:: ⍺÷0 → ⍺. ⍺←1 ⍝ default numerator. 5::↑∇/↓↑⍺ ⍵ ⍝ LENGTH:: stretch to fit. 11::⍺ ∇ ⍵+⍵=0 ⍝ DOMAIN:: increase divisor. ⍺÷⍵ ⍝ attempt division. }
Notice that some arguments may cause div to recur twice:
6 4 2 div 3 2 → 6 4 2 div 3 2 0 → 6 4 2 div 3 2 1 → 2 2 2
The final example shows the unwinding of the local environment before the error-guard's body is evaluated. Local name trap is set to describe the domain of its following error-guard. When an error occurs, the environment is unwound to expose trap's statically correct value.
add←{ trap←'domain' ⋄ 11::trap trap←'length' ⋄ 5::trap ⍺+⍵ } 2 add 3 ⍝ Addition succeeds 5 2 add 'three' ⍝ DOMAIN ERROR generated. domain 2 3 add 4 5 6 ⍝ LENGTH ERROR generated. length
Note:
Following the setting of an error-guard, subsequent function calls will disable tail call optimisation:
{ 22::'Oops!' ⍝ this error-guard means that ... tie←⍵ ⎕ftie 0 subfn tie ⍝ ... tail call not optimised }
One way to maintain the tail call optimisation in the presence of an error-guard is to isolate it within an inner function:
{ tie←{ 22::0 ⍝ error-guard local to inner fn ⍵ ⎕ftie 0 }⍵ tie=0:'Oops!' subfn tie ⍝ ... so this is a tail call }